package site.wtcc.originalfavor.func.aop;

import cn.hutool.core.lang.Assert;
import com.google.common.collect.Lists;
import com.sun.istack.internal.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import site.wtcc.originalfavor.biz.Result;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;

/**
 * 我的返回值全局处理
 *
 * @author hexiaodou
 * @date 2022/09/08 15:40:03
 */
@RequiredArgsConstructor
public class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

    private static final ArrayList<String> NOT_HANDLED_API_LIST = Lists.newArrayList(
            "*:/api/test/**/*",
            "POST:/auth/test");

    private final HandlerMethodReturnValueHandler returnValueHandler;

    @Override
    public boolean supportsReturnType(@NotNull MethodParameter methodParameter) {
        return this.returnValueHandler.supportsReturnType(methodParameter);
    }

    @Override
    public void handleReturnValue(Object returnValue, @NotNull MethodParameter returnType, @NotNull ModelAndViewContainer modelAndViewContainer, @NotNull NativeWebRequest nativeWebRequest) throws Exception {
        //returnValue 包含全局返回直接return
        if (returnValue instanceof Result) {
            this.returnValueHandler.handleReturnValue(returnValue, returnType, modelAndViewContainer, nativeWebRequest);
            return;
        }
        //是否包裹全局返回对象
        boolean handleReturnValue = true;

        HttpServletRequest nativeRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        Assert.notNull(nativeRequest);
        //assert关键字需要在运行时候显式开启才能生效，否则你的断言就没有任何意义。
        //用assert代替if是陷阱之二。assert的判断和if语句差不多，但两者的作用有着本质的区别：assert关键字本意上是为测试调试程序时使用的，但如果不小心用assert来控制了程序的业务流程，那在测试调试结束后去掉assert关键字就意味着修改了程序的正常的逻辑。
        //assert断言失败将面临程序的退出。
        //assert nativeRequest != null;
        String method = nativeRequest.getMethod();
        String servletPath = nativeRequest.getServletPath();
        // "POST:/api/test"
        String restfulPath = method + ":" + servletPath;
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String apiPath : NOT_HANDLED_API_LIST) {
            if (pathMatcher.match(apiPath, restfulPath)) {
                handleReturnValue = false;
                break;
            }
        }
        this.returnValueHandler.handleReturnValue(handleReturnValue ? Result.ofData(returnValue) : returnType, returnType, modelAndViewContainer, nativeWebRequest);
    }
}
